package com.magicrule.car.systemManage.configClass.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.magicrule.car.systemManage.configClass.redis.RedisUtil;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**
 * 
 * @ClassName:  ShiroConfiguration   
 * @Description:TODO(shiro的配置类)   
 * @author: hebb
 * @date:   2018年12月6日 下午10:01:27   
 *     
 * @Copyright: 2018 www.magicruler.com Inc. All rights reserved. 
 * 注意：本内容仅限于魔尺信息技术有限公司内部传阅，禁止外泄以及用于其他的商业目的
 */
@Order(21)
@Configuration
public class ShiroConfiguration {
	
	protected static Logger log = LoggerFactory.getLogger(ShiroConfiguration.class);  
	
	private static final String CACHE_KEY = "shiro_cache:";
	
	/**
	 * @Title: delegatingFilterProxy   
	 * @Description: TODO(shiro的filter配置)   
	 * @param: @return      
	 * @return: FilterRegistrationBean      
	 * @throws
	 */
	@Bean
	public FilterRegistrationBean<DelegatingFilterProxy> delegatingFilterProxy(){
		FilterRegistrationBean<DelegatingFilterProxy> filterRegistrationBean = new FilterRegistrationBean<DelegatingFilterProxy>();
		DelegatingFilterProxy proxy = new DelegatingFilterProxy();
		proxy.setTargetFilterLifecycle(true);
		proxy.setTargetBeanName("shiroFilter");
		filterRegistrationBean.setFilter(proxy);
		return filterRegistrationBean;
	}

	
	
	/**************************shiro session管理相关配置开始****************************/
	
	/**
	 * @Title: sessionIdCookie   
	 * @Description: TODO(定义seesionId对应的cookie)   
	 * @param: @return      
	 * @return: SimpleCookie      
	 * @throws
	 */
	@Bean(name = "sessionIdCookie")
	public SimpleCookie sessionIdCookie() {
		SimpleCookie cookie = new SimpleCookie("SID");
		cookie.setHttpOnly(true);
		cookie.setMaxAge(-1);
		return cookie;
	}
	
	/**
	 * 会话验证调度器
	 * @Title: sessionValidationScheduler   
	 * @Description: TODO(定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话)   
	 * @param: @return      
	 * @return: ExecutorServiceSessionValidationScheduler      
	 * @throws
	 */
	@Bean(name = "sessionValidationScheduler")
	public ExecutorServiceSessionValidationScheduler sessionValidationScheduler() {
		ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
		//设置调度时间间隔，单位毫秒，这里设置为15分钟；
		scheduler.setInterval(900000);
		return scheduler;
	}

	/**
	 * @Title: redisManager   
	 * @Description: TODO(配置RedisSessionDAO的redisManager,这里是单点redis)   
	 * @param: @return      
	 * @return: RedisManager      
	 * @throws
	 */
	@Bean(name="redisManager")
	public RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost("127.0.0.1:6379");
		redisManager.setTimeout(2000);
		return redisManager;
	}
	
	
	/**
	 * @Title: redisSessionDAO   
	 * @Description: TODO(shiro的SessionDao接口的实现，通过redis来存储/持久化session)   
	 * @param: @return      
	 * @return: RedisSessionDAO      
	 * @throws
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO() {
	    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
	    redisSessionDAO.setKeyPrefix(CACHE_KEY);
	    redisSessionDAO.setRedisManager(redisManager());
	    return redisSessionDAO;
	}

	
	/**
	 * @Title: redisCacheManager   
	 * @Description: TODO(shiro集成redis缓存管理器)
	 * @param redisManage)   
	 * @param: @param redisManager
	 * @param: @return      
	 * @return: RedisCacheManager      
	 * @throws
	 */
	@Bean
    public RedisCacheManager redisCacheManager(RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        //过期时间,单位是秒,默认是30分钟，这里设置24小时
        redisCacheManager.setExpire(86400);
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        redisCacheManager.setPrincipalIdFieldName("userName");
        return redisCacheManager;
    }

	
	/**
	 * 
	 * @Title: sessionManage   
	 * @Description: TODO(shiro的会话管理器)   
	 * @param: @return      
	 * @return: DefaultWebSessionManager      
	 * @throws
	 */
	@Bean(name = "sessionManager")
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		//session过期时间，单位毫秒，默认是30分钟
		sessionManager.setGlobalSessionTimeout(1800000);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionValidationScheduler(sessionValidationScheduler());
		sessionManager.setDeleteInvalidSessions(true);
		
		sessionManager.setSessionIdCookieEnabled(true);
		sessionManager.setSessionIdCookie(sessionIdCookie());
		
		sessionManager.setSessionDAO(redisSessionDAO());
		
		return sessionManager;
	}

	/**************************shiro session管理相关配置结束****************************/
	
	/**
	 * @Title: credentialsMatcher   
	 * @Description: TODO(配置自定义的密码匹配器)   
	 * @param: @param redisUtil
	 * @param: @return      
	 * @return: CredentialsMatcher      
	 * @throws
	 */
	@Bean(name="credentialsMatcher")
	public CredentialsMatcher credentialsMatcher(RedisUtil redisUtil){
		return new RetryLimitHashedCredentialsMatcher(redisUtil);
	}
	
	
	/**
	 * @Title: shiroDialect   
	 * @Description: TODO(与thymeleaf模板整合)   
	 * @param: @return      
	 * @return: ShiroDialect      
	 * @throws
	 */
	@Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

	
	/**
	 * @Title: rememberMeCookie   
	 * @Description: TODO(rememberMe Cookie相关属性)   
	 * @param: @return      
	 * @return: SimpleCookie      
	 * @throws
	 */
	@Bean(name = "rememberMeCookie")
	public SimpleCookie rememberMeCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		simpleCookie.setHttpOnly(true);
		//30天
		simpleCookie.setMaxAge(2592000);
		return simpleCookie;
	}
	
	/**
	 * @Title: rememberManager   
	 * @Description: TODO(rememberMe Cookie管理器，可以对rememberMe cookie加密)   
	 * @param: @return      
	 * @return: CookieRememberMeManager      
	 * @throws
	 */
	@Bean
	public CookieRememberMeManager rememberManager(){
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		//密钥是"magicRule2018"
		cookieRememberMeManager.setCipherKey(Base64.decode("bWFnaWNSdWxlMjAxOA=="));
		cookieRememberMeManager.setCookie(rememberMeCookie());
		return cookieRememberMeManager;
	}

	
	/**
	 * @Title: securityManager   
	 * @Description: TODO(配置核心安全事务管理器)   
	 * @param: @param authRealm
	 * @param: @param sessionManager
	 * @param: @return      
	 * @return: SecurityManager      
	 * @throws
	 */
	@Bean(name="securityManager")
	public SecurityManager securityManager(@Qualifier("authRealm")AuthRealm authRealm,@Qualifier("sessionManager")SessionManager sessionManager){
		log.info("------------shiro已经加载----------");
		DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
		securityManager.setRealm(authRealm);
        securityManager.setSessionManager(sessionManager);
        securityManager.setRememberMeManager(rememberManager());
        return securityManager;
	}
	

	
	/**
	 * @Title: authRealm   
	 * @Description: TODO(自定义的Realm)   
	 * @param: @param matcher
	 * @param: @param redisCacheManager
	 * @param: @return      
	 * @return: AuthRealm      
	 * @throws
	 */
	@Bean(name="authRealm")
	public AuthRealm authRealm(@Qualifier("credentialsMatcher")CredentialsMatcher credentialsMatcher,@Qualifier("redisCacheManager")RedisCacheManager redisCacheManager){
		AuthRealm authRealm=new AuthRealm();
        authRealm.setCredentialsMatcher(credentialsMatcher);
        authRealm.setCacheManager(redisCacheManager);
        return authRealm;
	}
	
	
	/**
	 * 
	 * @Title: shiroFilter   
	 * @Description: TODO(shiro过滤器)   
	 * @param: @param securityManager
	 * @param: @param sessionManager
	 * @param: @param redisCacheManager
	 * @param: @return      
	 * @return: ShiroFilterFactoryBean      
	 * @throws
	 */
	@Bean(name="shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")SecurityManager securityManager,@Qualifier("sessionManager")SessionManager sessionManager,@Qualifier("redisCacheManager")RedisCacheManager redisCacheManager){
		ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
		bean.setSecurityManager(securityManager);
		
		//自定义拦截器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        KickoutSessionControlFilter kickoutSessionControlFilter=new KickoutSessionControlFilter();
        kickoutSessionControlFilter.setCache(redisCacheManager);
        kickoutSessionControlFilter.setSessionManager(sessionManager);
        kickoutSessionControlFilter.setKickoutAfter(false);
        kickoutSessionControlFilter.setMaxSession(1);
        kickoutSessionControlFilter.setKickoutUrl("/login.html");
        filtersMap.put("kickoutSessionControlFilter",kickoutSessionControlFilter );
        bean.setFilters(filtersMap);
        
		
		//配置登录的url和登录成功的url
		bean.setLoginUrl("/login.html");
		bean.setSuccessUrl("/index.html");
		bean.setUnauthorizedUrl("/unauthor.html");
		
		//配置访问权限
		LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
		/**
		 * 对/static目录下的login.html,unauthor.html,exception.html可以匿名访问,/static是springboot默认的静态资源目录
		 * 对登陆/退出/异常页面可以匿名访问
		 */
        filterChainDefinitionMap.put("/login*", "anon"); 
        filterChainDefinitionMap.put("/loginUser", "anon"); 
        filterChainDefinitionMap.put("/logout","anon");
        //对/static/error下面的html文件不做验证，我们在其下面放着401.html，404.html
        filterChainDefinitionMap.put("/error/*.html", "anon");
        
        //表示需要用户已经身份验证/记住我登录的都可以访问
        filterChainDefinitionMap.put("/index*","anon");
        
        //自定义过滤器并发控制同时在线人数
        //filterChainDefinitionMap.put("/**/*", "kickoutSessionControlFilter");
        
        //表示任何路径都需要认证
        //filterChainDefinitionMap.put("/**/*","authc");
        filterChainDefinitionMap.put("/**/*","anon");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        
		return bean;
		
	}
	
	
	//Shiro生命周期处理器
	@Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
	
	//AOP式方法级权限检查
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    
    //启用shiro授权注解拦截方式
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }
    
    
	
	
	
}
